package com.smzd.ccms.core;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;

import com.smzd.ccms.domain.CompletConfig;
import com.smzd.ccms.domain.CompletParamConfig;
import com.smzd.utils.SimpleDateTimeUtil;

import freemarker.template.TemplateBooleanModel;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateNumberModel;
import freemarker.template.TemplateScalarModel;

public class ParamsWrapper {
	/**
	 * 输出的参数Map
	 */
	private Map<String, Object> outParams;
	/**
	 * 给进来的Tag参数Map
	 */
	private Map<String, Object> tagParams;
	/**
	 * 给进来的ModelMap的参数Map
	 */
	private Map<String, Object> modelParams;
	/**
	 * 小组件以及运行上下文实体包装
	 */
	private EntityWrapper entityWrapper;

	private HttpServletRequest request;

	/**
	 * 当前执行器
	 */
	private CompletConfig completConfig;

	public ParamsWrapper(Map<String, Object> tagParams, Map<String, Object> modelParams, EntityWrapper entityWrapper,
	        CompletConfig completConfig, HttpServletRequest request) {
		outParams = new HashMap<String, Object>();
		this.tagParams = tagParams;
		this.modelParams = modelParams;
		this.entityWrapper = entityWrapper;
		this.completConfig = completConfig;
		this.request = request;
	}

	/**
	 * 
	 * 参数的来源。有1-4四种来源，若配置为0，则按照既定的优先次序，先到先得。<br>
	 * <ul>
	 * <li>0: 默认次序来源：Tag &gt; Request &gt; PathVar &gt; Default(New Default)
	 * <li>1: New Default Only
	 * <li>2: Request Only
	 * <li>3: PathVar Only
	 * <li>4: Tag Only
	 * </ul>
	 * 
	 * @param key
	 * @return
	 */

	@SuppressWarnings("unchecked")
	public <T> T getParamValue(String key, T def) {
		Object value = null;
		if (completConfig != null) {
			CompletParamConfig cpc = completConfig.getCpConfig(key);
			if (cpc != null) {
				String alias = cpc.getAlias();
				if (StringUtils.isEmpty(alias)) {
					alias = key;
				}
				switch (cpc.getSource()) {
				case 0:
					value = getTagParam(alias);// First
					if (value == null) {
						value = getModelParamValue(alias);// Second
					}
					if (value == null) {
						value = getPathVariableValue(cpc);// Third
					}
					if (value == null) {
						value = cpc.varValue();// Fourth
					}
					if (value == null) {
						value = completConfig.getDefaultParamValue(key);
					}
					break;
				case 1: // New Default Only
					value = cpc.varValue();
					break;
				case 2: // Request Only
					value = getModelParamValue(alias);
					break;
				case 3:// PathVar Only
					value = getPathVariableValue(cpc);
					break;
				case 4:// Tag Only
					value = getTagParam(alias);
					break;
				default:
				}
				return value == null ? def : (T) value;
			}
		}
		value = getTagParam(key);
		if (value == null) {
			value = getModelParamValue(key);
		}
		if (value == null) {
			if (def == null) {
				value = getPathVariableValue(0, key);
			} else if (def.getClass().getName() == "java.lang.String") {
				value = getPathVariableValue(0, key);
			} else if (def.getClass().getName() == "java.lang.Integer") {
				value = getPathVariableValue(1, key);
			} else if (def.getClass().getName() == "java.lang.Double") {
				value = getPathVariableValue(2, key);
			} else if (def.getClass().getName() == "java.lang.Date") {
				value = getPathVariableValue(4, key);
			} else {
				value = getPathVariableValue(3, key);
			}
			if (value != null) {
				try {
					return (T) value;
				} catch (Exception e) {
					value = null;
				}
			}
		} else {
			if (def.getClass().getName() == "java.lang.String") {
				value = String.valueOf(value);
			} else if (def.getClass().getName() == "java.lang.Integer") {
				value = Integer.parseInt(String.valueOf(value));
			} else if (def.getClass().getName() == "java.lang.Double") {
				value = Double.parseDouble(String.valueOf(value));
			} else if (def.getClass().getName() == "java.lang.Date") {
				value = SimpleDateTimeUtil.parse(String.valueOf(value));
			} else {
				value = Boolean.parseBoolean(String.valueOf(value));
			}
		}
		if (value == null && completConfig != null) {
			value = completConfig.getCompletDefaultParamValue(key);
		}
		return value == null ? def : (T) value;
	}

	@SuppressWarnings("unchecked")
	private <T> T getPathVariableValue(CompletParamConfig cpc) {
		String key = cpc.getKey();
		int type = cpc.getType();
		Object value = getPathVariableValue(type, key);
		return (T) value;
	}

	private Object getPathVariableValue(int type, String key) {
		String pathvar = getModelPathVar(key);
		Object value = null;
		if (pathvar != null) {
			switch (type) {
			case 0:
				value = pathvar;
				break;
			case 1:
				value = Integer.parseInt(pathvar);
				break;
			case 2:
				value = Double.parseDouble(pathvar);
				break;
			case 3:
				value = Boolean.parseBoolean(pathvar);
				break;
			case 4:
				value = SimpleDateTimeUtil.parse(pathvar);
				break;

			default:
				value = null;
				break;
			}
		}
		return value;
	}

	/**
	 * 获取路径参数。路径参数是不会和Request参数混合在一起的，虽然都在modelParams里面。
	 * 
	 * @param key
	 * @return
	 */
	private String getModelPathVar(String key) {
		return (String) modelParams.get(CCMSConstant.KEY_PATH_VAR_PREFIX + key);
	}

	/**
	 * 这个接口用于获取输出数据的所有key值，用于备份大环境中的数据用的，这样不同的组件可以共享变量名而不用担心会出现冲突。
	 * 
	 * @return
	 */
	public Set<String> keySet() {
		return outParams.keySet();
	}

	/**
	 * 向输出的Map中添加数据
	 * 
	 * @param key
	 * @param value
	 */
	public void put(String key, Object value) {
		outParams.put(key, value);
	}

	/**
	 * 从输出的Map中获取数据
	 * 
	 * @param key
	 * @return
	 */
	public <T> T get(String key) {
		return getGTValue(outParams, key);
	}

	/**
	 * 从指定的Map中获取指定名称的参数值。这是一个通用方法。
	 * 
	 * @param map
	 * @param key
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private static <T> T getGTValue(Map<String, Object> map, String key) {
		if (map == null) {
			return null;
		}
		Object obj = map.get(key);
		if (obj != null) {
			return (T) obj;
		}
		return null;
	}

	/**
	 * 从Tag中获取数据。
	 * 
	 * @param key
	 * @return
	 */

	@SuppressWarnings("unchecked")
	private <T> T getTagParam(String key) {
		TemplateModel mode = getGTValue(tagParams, key);
		Object value = null;
		if (mode instanceof TemplateNumberModel) {
			try {
				value = ((TemplateNumberModel) mode).getAsNumber();
			} catch (TemplateModelException e) {
				e.printStackTrace();
			}
		} else if (mode instanceof TemplateScalarModel) {
			try {
				value = ((TemplateScalarModel) mode).getAsString();
			} catch (TemplateModelException e) {
				e.printStackTrace();
			}
		} else if (mode instanceof TemplateBooleanModel) {
			try {
				value = ((TemplateBooleanModel) mode).getAsBoolean();
			} catch (TemplateModelException e) {
				e.printStackTrace();
			}
		}
		return (T) value;
	}

	public <T> T getModelParamValue(String key) {
		return getGTValue(modelParams, key);
	}

	public Map<String, Object> getOutParams() {
		return outParams;
	}

	public void putAll(Map<String, Object> params) {
		outParams.putAll(params);
	}

	public EntityWrapper getEntityWrapper() {
		return entityWrapper;
	}

	public HttpServletRequest getRequest() {
		return request;
	}

	public void setRequest(HttpServletRequest request) {
		this.request = request;
	}
}
